package lormi.meipin.interceptor;

import lormi.meipin.exception.LMException;
import org.hibernate.validator.internal.engine.ValidatorImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.HandlerMethodSelector;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;
import org.springframework.web.servlet.support.RequestContextUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义方法入参级别验证
 * 
 * @author ThreeFeng
 * @date 2016年8月10日 下午4:18:43
 */

@SuppressWarnings("deprecation")
public class ValidationInterceptor implements HandlerInterceptor{

	private List<HandlerMethodArgumentResolver> argumentResolvers;

	@Autowired
	private Validator validator;

    @Autowired
	private RequestMappingHandlerAdapter adapter;

	private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256);
	private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(64);

    @Autowired
	public ValidationInterceptor(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
		argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
	}

	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws LMException {
		LocalValidatorFactoryBean validatorFactoryBean = (LocalValidatorFactoryBean) validator;
		ValidatorImpl validatorImpl = (ValidatorImpl) validatorFactoryBean.getValidator();
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		HandlerMethod method = (HandlerMethod) handler;
		Valid valid = method.getMethodAnnotation(Valid.class);//从方法中获取@valid注解
		if (valid != null) {//如果存在则对参数进行校验
			Class<?>[] groups = new Class<?>[0];
			MethodParameter[] parameters = method.getMethodParameters();//获取所有参数
			Object[] parameterValues = new Object[parameters.length];
			for (int i = 0; i < parameters.length; i++) {//遍历所有参数
				MethodParameter parameter = parameters[i];
				HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
				Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
				ModelAndViewContainer mavContainer = new ModelAndViewContainer();
				mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
				WebDataBinderFactory webDataBinderFactory;
				Object value ;
				try {
					webDataBinderFactory = getDataBinderFactory(method);
					value = resolver.resolveArgument(parameter, mavContainer, webRequest, webDataBinderFactory);
				} catch (Exception e) {
					throw new LMException(200004);
				}
				parameterValues[i] = value;
			}
			Set<ConstraintViolation<Object>> violations = validatorImpl.validateParameters(method.getBean(),
					method.getMethod(), parameterValues, groups);
			if (!violations.isEmpty()) {
				throw new LMException(violations.iterator().next().getMessage());
			}
		}
		return true;
	}

	private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
		Class<?> handlerType = handlerMethod.getBeanType();
		Set<Method> methods = this.initBinderCache.get(handlerType);
		if (methods == null) {
			methods = HandlerMethodSelector.selectMethods(handlerType,
					RequestMappingHandlerAdapter.INIT_BINDER_METHODS);
			this.initBinderCache.put(handlerType, methods);
		}
		List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
		for (Method method : methods) {
			Object bean = handlerMethod.getBean();
			initBinderMethods.add(new InvocableHandlerMethod(bean, method));
		}
		return new ServletRequestDataBinderFactory(initBinderMethods, adapter.getWebBindingInitializer());
	}

	//参数组装
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
			for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
				if (methodArgumentResolver.supportsParameter(parameter)) {
					result = methodArgumentResolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}

	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {

	}

	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {

	}

}
